/*************************************************************************
	> File Name: keys.c
	> Author: life
	> Created Time: 2021年07月4日 星期日 22时04分23秒
 ************************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>

#define BCM2711_GPIO_BASE  0xfe200000
#define DEVICE_NAME     "rpi-keys"  /* 加载模式后，执行”cat /proc/devices”命令看到的设备名称 */
static unsigned int * gpio_va;
static int major = 0; //主设备号

static struct class *keys_class;
static struct device *keys_class_devs[2];

#define KEY1	5
#define KEY2	6
//对 int 型指针操作 +1 等于地址 +4 所以要先 /4
#define GPFSEL0 (*(volatile unsigned int *)(gpio_va + (0x0/4)))
#define GPFSEL1 (*(volatile unsigned int *)(gpio_va + (0x4/4)))
#define GPFSEL2 (*(volatile unsigned int *)(gpio_va + (0x8/4)))
#define GPFSEL3 (*(volatile unsigned int *)(gpio_va + (0xc/4)))
#define GPFSEL4 (*(volatile unsigned int *)(gpio_va + (0x10/4)))
#define GPFSEL5 (*(volatile unsigned int *)(gpio_va + (0x14/4)))
#define GPSET0  (*(volatile unsigned int *)(gpio_va + (0x1c/4)))	//置位寄存器
#define GPSET1  (*(volatile unsigned int *)(gpio_va + (0x20/4)))
#define GPCLR0  (*(volatile unsigned int *)(gpio_va + (0x28/4)))	//清零寄存器
#define GPCLR1  (*(volatile unsigned int *)(gpio_va + (0x2c/4)))
#define GPLEV0	(*(volatile unsigned int *)(gpio_va + (0x34/4)))	//类似与STM32的IDR
#define GPLEV1	(*(volatile unsigned int *)(gpio_va + (0x38/4)))	//类似与STM32的IDR

static int rpi4_keys_open(struct inode *inode, struct file *file)
{
	//选择树莓派GPIO5 6 来做实验
	int minor = MINOR(inode->i_rdev);
	switch(minor)
	{
		case 0: /* /dev/keys */
		{
			GPFSEL0 &= ~(7<<(3*KEY1)); //清零 GPIO3   每3bit控制一个GPIO
			GPFSEL0 &= ~(7<<(3*KEY2)); //清零 GPIO4   每3bit控制一个GPIO
			break;
		}
		case 1: /* /dev/key1 */
		{
			
			GPFSEL0 &= ~(7<<(3*KEY1)); //清零 GPIO3   每3bit控制一个GPIO
			break;
		}
		case 2: /* /dev/key2 */
		{
			
			GPFSEL0 &= ~(7<<(3*KEY2)); //清零 GPIO4   每3bit控制一个GPIO
			break;
		}
		default :
		break;

	}
	printk(KERN_INFO"respberry keys open\n");
    return 0;
}

static ssize_t rpi4_keys_read (struct file *file, char __user *buff, size_t count, loff_t *offp)
{
	
	printk(KERN_INFO"rpi4_keys_read \n");

	int value;

	value = GPLEV0;

	if ( copy_to_user(buff, (const void *)&value, 1) !=0 )
	{
		printk(KERN_INFO"copy_to_user error \n");
		return -1;
	}
	return 0;
}

/* 这个结构是字符设备驱动程序的核心
 * 当应用程序操作设备文件时所调用的open、read、write等函数，
 * 最终会调用这个结构中指定的对应函数
 */
static struct file_operations rpi4_keys_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏，推向编译模块时自动创建的__this_module变量 */
    .open   =   rpi4_keys_open, 
	.read	=	rpi4_keys_read,	   
};

static int __init keys_init(void)
{
	 /* 注册字符设备
     * 参数为主设备号、设备名字、file_operations结构；
     * 这样，主设备号就和具体的file_operations结构联系起来了，
     * 操作主设备为LED_MAJOR的设备文件时，就会调用s3c24xx_keys_fops中的相关成员函数
     * LED_MAJOR可以设为0，表示由内核自动分配主设备号
     */
	int minor;
	
    gpio_va = ioremap(BCM2711_GPIO_BASE, 0xF0);
	if (!gpio_va) {
		return -EIO;
	}
	printk (KERN_INFO"global_gpio = 0x%lx\n", (unsigned int)gpio_va);
	major = register_chrdev(major, DEVICE_NAME, &rpi4_keys_fops);
	printk(KERN_INFO "major ; %d\n",major);
	if (major < 0) {
		printk(DEVICE_NAME " can't register major number\n");
		return major;
	}
	
	keys_class = class_create(THIS_MODULE, "mykeys");//注册class 让代码在dev自动生成设备
	if (IS_ERR(keys_class))
		return PTR_ERR(keys_class);

	keys_class_devs[0] = device_create(keys_class, NULL, MKDEV(major, 0), NULL, "keys");

	for (minor = 1; minor < 3; minor++)
	{
		keys_class_devs[minor] = device_create(keys_class, NULL, MKDEV(major, minor), NULL, "key%d", minor);
		if (unlikely(IS_ERR(keys_class_devs[minor])))
			return PTR_ERR(keys_class_devs[minor]);
	}
	printk(KERN_INFO"raspberry keys initialized\n");
	return 0;
}

static void __exit keys_exit(void)
{
	int minor;
	for(minor = 0; minor < 3; minor++ )
		device_unregister(keys_class_devs[minor]);
	class_destroy(keys_class);
	unregister_chrdev(major, DEVICE_NAME);
	iounmap(gpio_va);
	printk(KERN_INFO"keys_exit exit\n");
}
module_init(keys_init);
module_exit(keys_exit);

/* 描述驱动程序的一些信息，不是必须的 */
MODULE_AUTHOR("ChenBlue");
MODULE_VERSION("0.1.0");
MODULE_DESCRIPTION("raspberry KEY Driver");
MODULE_LICENSE("GPL");


